/**
 * \file msem182.c
 *
 * Purpose: 	Convenience functions for Semaphores
 *
 * Motivation:	The semop function is hard to use and provides
 *              functionality which is seldom used or even annoying
 *		(like the fact that 0 isn't a nop). These functions
 *		should provide a cleaner interface.
 *
 * Modification history:
 *  2.5 Use of semctl with enum semun
 *	2.2	Changed order of parameters in mseminit.
 *	2.1	Changed the semantics of mP and mV. The arguments now
 *		specify the index of the semaphore to operate on, 
 *		not the operation.
 *	1.1	got the version from the solution to Ex. 2.11 
 *		(which was derived from an out-of-date version of 
 *		sem182.c which had been heavily hacked by gusti) and
 *		adapted it to the STBS and the newest version of
 *		sem182.c.
 *
 * \version $Revision: 1.2 $
 * URL: $HeadURL$
 * Last Modified: $Author: bernd $
 */

#include "msem182.h"

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>


/**< Generate the CVS-Id of this file just in case someone needs it. */
#define CVSID	 "$Id: msem182.c,v 1.2 2005/12/18 20:58:19 bernd Exp $"
#define msem182_c_sccs_id	"@(#)	msem182.c	2.5	2002/03/05	rk"

/*
 * Exported functions
 */
int mseminit(const key_t key, const int perm, const int nsems, ...)
{
#if !defined(__GNU_LIBRARY__) || defined(_SEM_SEMUN_UNDEFINED)
     union semun {
         int val;                    /**< value for SETVAL */
         struct semid_ds *buf;       /**< buffer for IPC_STAT, IPC_SET */
         unsigned short int *array;  /**< array for GETALL, SETALL */
     };
#endif
    int     semid;
    union semun semarg;
    va_list ap;
    int     i;

    semid = semget (key, nsems, IPC_CREAT | IPC_EXCL | (perm & 0666));
    if (semid == -1) {
        return -1;
    }

    /* Initialize Semaphore */
    va_start (ap, nsems);
    for (i = 0; i < nsems; i ++) {
        semarg.val = va_arg (ap, int);
        if (semctl (semid, i, SETVAL, semarg) == -1) {
           const int save_errno = errno;
           (void) msemrm(semid);
           errno = save_errno;
           return -1;
        }
    }
    va_end (ap);
    
    return semid;
}


int msemgrab (const key_t key, const int nsems)
{
    return semget (key, nsems, 0);
}


int msemrm(const int semid) 
{
    return semctl (semid, 0, IPC_RMID, 0);
}


int mV(const int semid, const int nsems, ...)
{  
    struct sembuf   *semp;
    int             i;
    va_list         ap;
    int             rv;

    semp = calloc (nsems, sizeof(*semp));
    if (semp == NULL) {
        return -1;
    }

    va_start (ap, nsems);
    for (i = 0; i < nsems; i++) {
        semp[i].sem_num = va_arg (ap, int);
        semp[i].sem_op = 1;
    }
    va_end (ap);

    rv = 0;
    if (semop(semid, semp, nsems) == -1) {
        rv = -1;
    }
    free (semp);
    return rv;
}


int mP(const int semid, const int nsems, ...)
{  
    struct sembuf   *semp;
    int             i;
    va_list         ap;
    int             rv;

    semp = calloc (nsems, sizeof (*semp));
    if (semp == NULL) {
        return -1;
    }

    va_start (ap, nsems);
    for (i = 0; i < nsems; i ++) {
        semp[i].sem_num = va_arg (ap, int);
        semp[i].sem_op = -1;
    }
    va_end (ap);

    rv = 0;
    if (semop(semid, semp, nsems) == -1) {
        rv = -1;
    }
    free (semp);
    return rv;
}

/*
 * Local Variables:
 * mode: c
 * c-indent-tabs-mode: nil
 * c-basic-offset: 4
 * End:
 */
